package org.splink.ant;

import java.io.File;
import java.io.FileWriter;
import java.util.List;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;

/**
 * An ant task designed to detect and inject the filesizes of elements
 * referenced within an xml file which contains elements with the folloing structure: 
 * <pre>
 * 	<node path="">
 * 		<inner src="" filesize="">
 * 		<inner src="" filesize="">
 * 	</node>
 * </pre>
 * 
 * @author Max Kugland
 */
public class SizeInjectorTask extends Task {
	private String separator = System.getProperty("file.separator");
	private String baseDir;

	private String file;
	private String resourceBaseDir = "";

	/**
	 * Set the xml file in which to inject the filesizes
	 * 
	 * @param file
	 *            the path to the file from the projects basedir
	 */
	public void setFile(String file) {
		baseDir = getProject().getBaseDir() + separator;
		this.file = baseDir + replaceSeparator(file);
	}

	/**
	 * Set the basedir of the resources referenced with the xml file
	 * 
	 * @param resourceBaseDir
	 */
	public void setResourceBaseDir(String resourceBaseDir) {
		this.resourceBaseDir = baseDir + resourceBaseDir + separator;
	}

	@Override
	public void execute() throws BuildException {
		log("Resource file: " + file, Project.MSG_INFO);
		log("Resources basedir is " + resourceBaseDir, Project.MSG_INFO);
		try {
			File f = new File(file);
			Document doc = new SAXBuilder().build(f);

			process(doc.getRootElement());
			writeFile(doc, f);
		} catch (Exception e) {
			throw new BuildException(e.getMessage());
		}
	}

	/**
	 * Recursively process the xml file and inject the resources filesizes
	 * 
	 * @param element
	 *            used to traverse the xml tree
	 */
	@SuppressWarnings("unchecked")
	private void process(Element element) {
		String path = element.getAttributeValue("path");
		if (path == null)
			path = "";

		List<Element> childs = element.getChildren();
		for (Element e : childs) {
			String src = e.getAttributeValue("src");

			if (path != null) {
				path = replaceSeparator(path);
				if (src != null) {
					src = replaceSeparator(src);
					File f = new File(resourceBaseDir + path + src);
					if (f.exists()) {
						log("injecting " + f.length() + " bytes into " + path
								+ src, Project.MSG_INFO);
						e.setAttribute("filesize", String.valueOf(f.length()));
					} else
						log("file not found: " + path + src, Project.MSG_WARN);
				} else if (e.getAttributeValue("filesize") != null) {
					e.setAttribute("filesize", "0");
				}
			} else {
				log("path not found: " + path + src, Project.MSG_WARN);
			}

			process(e);
		}
	}

	/**
	 * Adjust the incoming path to match the operating systems file separator
	 * 
	 * @param path
	 *            the path to adjust
	 * @return the result of the adjustment
	 */
	private String replaceSeparator(String path) {
		path = path.replace("/", separator);
		path = path.replace("\\", separator);
		return path;
	}

	/**
	 * Write the processed xml document to disk
	 * 
	 * @param doc
	 *            the xml document
	 * @param file
	 *            the file to store the xml doc into
	 */
	private void writeFile(Document doc, File file) {
		XMLOutputter out = new XMLOutputter();
		try {
			FileWriter writer = new FileWriter(file);
			out.output(doc, writer);
			writer.flush();
			writer.close();

		} catch (Exception e) {
			log(e.getMessage(), Project.MSG_INFO);
		}
	}
}
